/*
 * Copyright (C) 2020 The LINN Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.juqimiao.linn.command;

import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import com.juqimiao.linn.exception.ExceptionHandlerFactory;
import com.juqimiao.linn.interception.InterceptorKeeperI;
import com.juqimiao.linn.logging.Logger;
import com.juqimiao.linn.transfer.Command;
import com.juqimiao.linn.transfer.Response;
import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 表示命令执行的所需的信息类。
 *
 * @author  Collin Cai 2020/2/24
 */
@Component
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CommandInvocation {
    // private static Logger logger

    // ---- Fields ----------/

    @Setter
    @Getter
    private CommandExecutorI commandExecutor;
    @Setter
    private Iterable<CommandInterceptorI> preInterceptors = new ArrayList<>();
    @Setter
    private Iterable<CommandInterceptorI> postInterceptors = new ArrayList<>();

    private CommandHub commandHub;

    private InterceptorKeeperI interceptorKeeper;

    private Logger logger = Logger.getLogger(CommandRegistry.class);
    // ---- Constructor ----------/

    /**
     * 构造器。
     *
     * @param commandHub 一个命令集线器。
     */
    @Autowired
    public CommandInvocation(CommandHub commandHub, InterceptorKeeperI interceptorKeeper) {
        this.commandHub = commandHub;
        this.interceptorKeeper = interceptorKeeper;
        if (interceptorKeeper != null) {
            this.preInterceptors = interceptorKeeper
                    .getInterceptors(CommandInterceptorI.class, true);
            this.postInterceptors = interceptorKeeper
                    .getInterceptors(CommandInterceptorI.class, false);
        }
    }

    // ---- Methods ----------/

    /**
     * 命令实际执行方法。
     *
     * @param command 一个命令对象。
     * @return 一个响应对象。
     */
    public Response invoke(Command command) {
        logger.info(String.format("Begin execute command %s ", command.getClass().getName()));
        Response response = null;
        try {

            ImmutableSet<CommandInterceptorI> preInterceptorsSet = FluentIterable.from(preInterceptors).toSet();
            preInterceptorsSet.forEach(interceptor -> interceptor.preIntercept(command));
            response = commandExecutor.execute(command);
            /** response有可能为Null。客户端用户实现中, execute方法直接返回为null*/
            if (response == null) {
                throw new NullPointerException();
            }
        } catch (Exception e) {
            response = getResponseInstance(command);
            //Handler the failure response.
            ExceptionHandlerFactory.getHandler().handleException(command, response, e);
        } finally {
            executePostInterceptor(response, command);
        }
        logger.info(String.format("End execute command %s and Response %s", command, response));
        return response;
    }

    /**
     * 获取从命令集线器中一个对象实例.
     *
     * @param command 一个命令对象.
     * @return 一个相应对象.
     */
    private Response getResponseInstance(Command command) {
        Class responseClz = commandHub.getResponseKeeper().get(command.getClass());
        try {
            return (Response) responseClz.newInstance();
        } catch (Exception e) {
            return new Response();
        }
    }

    /**
     * 执行后置拦截器.
     *
     * @param response 一个响应对象.
     * @param command  一个命令对象.
     */
    private void executePostInterceptor(Response response, Command command) {
        /* Todo: Replace FluentIterable */
        Set<CommandInterceptorI> postInterceptorsSet = FluentIterable.from(postInterceptors).toSet();
        postInterceptorsSet.forEach(interceptor -> interceptor.postIntercept(response, command));
    }
}

